;>FileCore45

        TTL     "Adfs45 - OsFile"

;Each entry in OsFile table is 32 bits as below
; bits 0-7   restrictions for FullLookUp, NotNulName assumed for all
; bits 14-15 not found action
;  00 => just return R0=0
;  01 => generate not found error
;  10 => not found error only if not final term of path otherwise enter routine
; bits 16-31 offset of code

MayExist        * 0
MustExist       * 1 :SHL: 14
SpecialNotFound * 1 :SHL: 15

NotFoundBits * MustExist :OR: SpecialNotFound

 MACRO
        OsFileEntry  $Name,$Restrict,$NF
       [ "$Name"="Load"
        ASSERT  .-OsFileTable=FirstUnknown_fsfile*4
       |
        ASSERT  .-OsFileTable=fsfile_$Name*4
       ]
        ASSERT  (DoOsFile$Name-OsFileJump-8)<1 :SHL: 16
        ASSERT  $Restrict<&100                          ;for use of AND &FF
 &       ((DoOsFile$Name-OsFileJump-8):SHL:16) :OR: $Restrict :OR: NotNulName :OR: $NF
 MEND

OsFileTable
        OsFileEntry  Save,NotLastWild :OR: NotLastUp :OR: DirToBuffer,SpecialNotFound
        OsFileEntry  WriteInfo,DirToBuffer,MayExist
        OsFileEntry  WriteLoad,DirToBuffer,MayExist
        OsFileEntry  WriteExec,DirToBuffer,MayExist
        OsFileEntry  WriteAttr,DirToBuffer,MayExist
        OsFileEntry  ReadInfo,0,MayExist
        OsFileEntry  Delete,NotLastWild :OR: NotLastUp :OR: DirToBuffer,MayExist
        OsFileEntry  Create,NotLastWild :OR: NotLastUp :OR: DirToBuffer,SpecialNotFound
        OsFileEntry  CreateDir,NotLastWild :OR: NotLastUp :OR: DirToBuffer,SpecialNotFound
        OsFileEntry  ReadInfoNoLen,0,MayExist
        OsFileEntry  ReadBlockSize,MustBeFile,MustExist
;
        OsFileEntry  Load,MustBeFile,MustExist

; >>>>>>>>>>>
; OsFileEntry
; >>>>>>>>>>>

OsFileEntry
        ; 'safe' (read only) OSFiles
        TEQ     r0, #fsfile_Load
        BEQ     %FT10
        TEQ     r0, #fsfile_ReadInfo
        BEQ     %FT15
        Entry   Flag,Dormant    ;leaves SB,LR stacked
        B       %FT20
10
        Entry   Flag            ;leaves SB,LR stacked
        B       %FT20
15
        Entry   Flag,Light      ;leaves SB,LR stacked
20
        MOV     R6,R0
        BL      MyOsFile
        MOVVC   R0,R6
        sbaddr  R6,Opt1Buffer,VC
        BLVS    FindErrBlock    ; (R0->R0,V)
        BL      FileCoreExit
        Pull    "SB,PC"


; ========
; MyOsFile
; ========

; registers as MOS OsFile with R0 moved to R6 to free R0 for result code

; entry:
;  R1 name ptr
;  R2 load address
;  R3 exec address
;  R4 length ( start for save/create )
;  R5 atts   ( end for save/create )
;  R6 reason code

; exit:
; IF error V set, R0 result
; ELSE R0 ?
;  R1-R5 file atts if successful
;  R6 file type if successful or not found allowed

MyOsFile ROUT
 [ Debug7
        DLINE   "name     load     exec     len/beg  atts/end reason   - entering MyOsFile"
        DREG    R1,,cc
        DREG    R2,,cc
        DREG    R3,,cc
        DREG    R4,,cc
        DREG    R5,,cc
        DREG    R6
 ]

        Push    "R1,R6,R7-R11,LR" ;R6 is dummy for file type

        AND     R6, R6, #&FF
        CMPS    R6, #FirstUnknown_fsfile   ;valid CSD relative ?
        RSBCSS  LR, R6, #fsfile_Load-1
        MOVCS   R0, #BadParmsErr
        BCS     %FT95                   ;bad reason code
        TEQS    R6, #fsfile_Load
        MOVEQ   R6, #FirstUnknown_fsfile

        Push    "R2-R6"
        Pull    "R7-R11"

        baddr   LR, OsFileTable
        LDR     LR, [LR,R6,LSL #2]

        AND     R2, LR, #&FF
        Push    "LR"
        BL      FullLookUp      ;(R1,R2->R0-R6,C,V)
        BVS     %FT10
        MOVCC   R2, #1
        MOVCS   R2, #2          ;file type=1/2 for file/dir
        STR     R2, [SP,#8]     ;put file type in return register set
        B       %FT20
10
        MOV     R2, #0          ;not found file type
        STR     R2, [SP,#8]     ;put file type in return register set
        LDR     LR, [SP]
        TEQS    R0, #NotFoundErr
        BNE     %FT85
        ANDS    LR, LR, #NotFoundBits
        MOVEQ   R0,#0
        TEQS    LR, #SpecialNotFound
        BNE     %FT85
;must be special not found action
        BL      ThisChar        ;(R1->R0,C)
        MOVCC   R0, #NotFoundErr ;not terminator => not last term
        BCC     %FT85
20
        LDR     R0, [SP,#4]     ;string start
        BL      TermStart       ;backtrack to start of last term (R0,R1->R1)
        ASSERT  OldDirEntrySz<=32
        ASSERT  NewDirEntrySz<=32
        TEQS    R2, #0          ;Z=1 <=> not found

        Push    "R4,R6,R7"      ;save name for *OPT 1
        MOVEQ   R4, R1
        MOV     R6, #NameLen
        sbaddr  R7, Opt1Buffer
        BL      PutMaskedString ;(R3,R4,R6,R7)
        MOV     LR, #0
        STRB    LR, [R7]
        Pull    "R4,R6,R7"

        Pull    "LR"
        ASSERT  NewDirEntrySz<=28
        SUB     SP, SP, #28     ;space for copy of dir entry for delete

OsFileJump
        MOV     R0, PC
        ADD     LR, R0, LR, LSR #16     ;branch to routine
        MOV     PC, LR

;enter OsFile routine

; results of parsing name
;  R2    file type, Z=1<=>not found, C=1 <=> dir
;  R3    dir ind disc address
;  R4 -> dir entry (next if not found)
;  R5 -> dir start
;  R6 -> dir end

; OsFile params
;  R1 -> name ( last term of path )
;  R7    load
;  R8    exec
;  R9    length / start
;  R10   atts / end
;  R11   reason code

OsFileBack              ;R0,V result, R3=dir disc address, R4->dir entry
        BVS     %FT80
 [ Module_Version >= 205
        BL      ReadLoad        ;(R3,R4->LR)
 |
        BL      ReadLoad        ;(R4->LR)
 ]
        MOV     R2, LR
OsFileBack2
        BVS     %FT80
        BL      ReadExtAtts     ;(R3,R4->LR)
        MOV     R10,LR
        BL      ReadExec        ;(R4->LR)
        MOV     R11,LR
        BL      ReadLen         ;(R3,R4->LR)
        MOV     R4, LR
        MOV     R3, R11
        MOV     R5, R10
        MOV     R0, #0
80
        ADD     SP, SP, #28-4   ;reclaim space for copy of dir entry
85
        ADD     SP, SP, #4
95
        BL      SetVOnR0
 [ Debug7
        Pull    "R1,R6"
        DLINE   "name     load     exec     length   atts     ftype    result - leave MyOsFile"
        DREG    R1,,cc
        DREG    R2,,cc
;        DREG   R3,,cc
        DREG    R4,,cc
        DREG    R5,,cc
        DREG    R6,,cc
        BVC     %FT01
        DREG    R0,,cc
01
        DLINE   ""
        Pull    "R7-R11,PC"
 |
        Pull    "R1,R6,R7-R11,PC"
 ]


DoOsFileSave ROUT
DoOsFileCreate
        BL      SaveCreate      ;(R1-R11,Z,C->R0,V)
        B       OsFileBack

; ============
; LockedOrOpen
; ============

; entry
;  R3 dir
;  R4 -> dir entry

; exit NCZ preserved, IF error V set, R0 error
; Any buffers on file discarded if not open.

LockedOrOpen ROUT
        Push    "R8,R9,LR"
        BL      ReadIntAtts     ;(R3,R4->LR)
        TSTS    LR, #IntLockedBit
        MOVNE   R0, #LockedErr
        BNE     %FT10
        BL      OpenCheck       ;(R3,R4->R0,R8,R9,LR,Z)
        BEQ     %FT10
        LDR     LR, [R9, #FcbExtHandle]
        TEQS    LR, #0
        BNE     %FT10
        BL      FreeFcb         ;(R8,R9->R0,V)
        MOV     R0, #0
10
        BL      SetVOnR0
        Pull    "R8,R9"
        B       PullLinkKeepV


DoOsFileWriteInfo  ROUT
DoOsFileWriteLoad
DoOsFileWriteExec
DoOsFileWriteAttr
        CMPS    R4, #0
        BEQ     %FT90

        ASSERT  fsfile_WriteInfo<fsfile_WriteLoad
        ASSERT  fsfile_WriteInfo<fsfile_WriteExec
        ASSERT  fsfile_WriteInfo<fsfile_WriteAttr

        CMPS    R11,#fsfile_WriteInfo+1     ;C=0 <=> WriteInfo
        TEQS    R11,#fsfile_WriteLoad
        MOVLS   R0, R7
        BLLS    WriteLoad               ;(R0,R3,R4) IF WriteInfo OR WriteLoad
        TEQS    R11,#fsfile_WriteExec
        MOVLS   R0, R8
        BLLS    WriteExec               ;(R0,R4) IF WriteInfo OR WriteExec
        TEQS    R11,#fsfile_WriteAttr
        MOVLS   R0, R10
        BLLS    WriteExtAtts            ;(R0,R4) IF WriteInfo OR WriteAttr
        LDR     LR, BufDir
        CMPS    LR, #-1                 ;If buffer still valid => no change
        BLNE    ClearV
        BNE     %FT80
        BL      IncObjSeqNum            ;(R3-R5)
        BL      WriteDir        ; will revalidate DirBuf if success (->R0,V)
                                ; Ensures new Id if necessary
80
        B       OsFileBack

90
        ; Setting the attributes of the root object
        ; Assume can't set any of the attributes, but can ensure
        ; a new Id.
        BL      DiscAddToRec            ; (R3->LR)
        LDRB    r0, [lr, #DiscFlags]
        ORR     r0, r0, #NeedNewIdFlag
        STRB    r0, [lr, #DiscFlags]
        BL      EnsureNewId
        B       OsFileBack



DoOsFileReadInfoNoLen
DoOsFileReadInfo
        BL      ClearV
        B       OsFileBack

DoOsFileReadBlockSize
        BL      ReadAllocSize   ;(R3->LR)
        MOV     r2, lr
        B       OsFileBack2

DoOsFileDelete
;check various restrictions

        BL      LockedOrOpen
        BVS     %FT99           ;refuse to delete open files

        TEQS    R2, #2          ;file type=2 <=> trying to delete directory
        BNE     %FT10
        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        LDR     R0, UserRootDir
        TEQS    R0, LR
        MOVEQ   R0, #CantDelUrdErr
        BEQ     %FT95
        LDR     R0, LibDir
        TEQS    R0, LR
        MOVEQ   R0, #CantDelLibErr
        BEQ     %FT95
        LDR     R0, CurDir
        TEQS    R0, LR
        MOVEQ   R0, #CantDelCsdErr
        BEQ     %FT95
        LDR     R1, BackDir     ;IF deleting PSD  THEN  PSD := CSD
        TEQS    R1, LR
        STREQ   R0, BackDir
        Push    "R3"
        MOV     R3, LR
        BL      GetDir          ;(R3->R0,R5,R6,V)
        Pull    "R3"
        BVS     %FT99
        LDRB    LR, [R5,#DirFirstEntry]
        TEQS    LR, #0
        MOVNE   R0, #DirNotEmptyErr
        BNE     %FT95
        BL      InvalidateBufDir ;so that dir about to delete wont be cached
        BL      GetDir          ;(R3->R0,R5,R6,V)
        BVS     %FT99
10
        MOV     R0, R4          ;make copy of dir entry
        MOV     R1, SP          ;space has already been reserved
        ASSERT  NewDirEntrySz>=OldDirEntrySz
        MOV     R2, #NewDirEntrySz
        BL      BlockMove       ;(R0-R2)

        BL      BeforeAlterFsMap ;(R3->R0,V)
        BLVC    RemoveDirEntry  ;(R3-R5->R0,V)
        BLVC    WriteDirThenFsMap ;(->R0,V)
        MOV     R4, SP          ;-> copy of dir entry
        BL      UnlockMap
        B       %FT99
95
        BL      SetVOnR0
99
        B       OsFileBack

; ==========
; WriteNames
; ==========

;entry
; EQ <=> new map
; R5 dir start
; R6 dir end

WriteNames
        Push    "R0,R1,LR"
        LDREQ   R1, ="N" :OR: ("i" :SHL:8) :OR: ("c" :SHL:16) :OR: ("k" :SHL:24) ;Nick
        LDRNE   R1, ="H" :OR: ("u" :SHL:8) :OR: ("g" :SHL:16) :OR: ("o" :SHL:24) ;Hugo
        ADD     R0, R5, #StartName
        BL      WriteWord       ;(R0,R1)
        ADD     R0, R6, #EndName
        BL      WriteWord
        Pull    "R0,R1,PC",,^


DoOsFileCreateDir ROUT
        BEQ     %FT05           ;not found
        MOVCC   R0, #TypesErr   ;if obj already exists only moan if was file
        MOVCS   R0, #0
        BL      SetVOnR0
        B       %FT99

05
        BL      IsDirFull       ;(R3,R5,R6->R0,V)
        BLVC    BeforeAlterFsMap;(R3->R0,V)
        BVS     %FT99
        BL      TestDir         ;(R3->LR,Z)
        MOVEQ   R10,#NewDirSize
        MOVNE   R10,#OldDirSize
        BL      ClaimFreeSpace  ;(R3,R10->R0,R2,V)
        BVS     %FT95
        BL      TryCacheDirBuf  ;(R3) will create dir in dir buffer
        Push    "R1-R4"
        MOV     R0, R5
        MOV     R1, R10
        BL      ZeroRam         ;(R0,R1)
        ADD     R6, R5, R1      ;->buf end
        BL      TestMap         ;(R3->Z)
        BL      WriteNames      ;(R5,R6,Z)
        BL      WriteParent     ;(R3,R6)
        LDR     R1, [SP]        ;->Name
        BL      TestDir         ;(R3->LR,Z)
        ADDEQ   R4, R6, #NewDirName
        ADDNE   R4, R6, #OldDirName
        BL      WriteName       ;enter name (R1,R4)
        ADDEQ   R4, R6, #NewDirTitle
        ADDNE   R4, R6, #OldDirTitle
        BL      WriteName       ;enter title=name (R1,R4)
        BL      TestDirCheckByte;(R3,R5,R6->LR)
        STRB    LR, [R6,#DirCheckByte]

        MOV     R1, #WriteSecsOp
        MOV     R3, R5
        MOV     R4, R10
        BL      IndDiscOp       ;write out new dir (R1-R4->R0-R5,V)
        Pull    "R1-R4"

        STRVC   R2, BufDir
        BLVC    TryCacheDirBuf  ;(R3)

        BLVC    GetDir          ;get back parent dir (R3->R0,R5,R6,V)
        BLVC    MakeDirSpace    ;(R3-R6)
        MOVVC   R0, #DirBit     ;default creation atts D
        BLVC    MakeDirEntry    ;(R0-R8,R10)
        BLVC    WriteFsMapThenDir ;(->R0,V)
95
        BL      UnlockMap
99
        B       OsFileBack


DoOsFileLoad ROUT
        BL      ReadIntAtts             ;check read access (R3,R4->LR)
        TSTS    LR, #ReadBit
        MOVEQ   R0, #AccessErr
        BEQ     %FT95
        Push    "R3,R4"                 ;save ptr to dir entry
        MOV     R1, #ReadSecsOp         ;ALLOW ESCAPE AND TIMEOUT
        BL      ReadIndDiscAdd          ;(R3,R4->LR)
        MOV     R2, LR
        Push    "R8"
        BL      OpenCheck               ;(R3,R4->R0,R8,R9,Z)
        Pull    "R8"
        LDRNE   LR, [R9, #FcbExtHandle]
        TEQNES  LR, #0
        LDRNEB  LR, [R9,#FcbFlags]
        TSTNES  LR, #WriteBit           ;refuse to load files open for update
        ADDNE   SP, SP, #2*4
        BNE     %FT95
        TSTS    R8, #&FF        ;bottom byte of exec <> 0 => use file load address
 [ Module_Version >= 205
        BLNE    ReadLoad                ;(R3,R4->LR)
 |
        BLNE    ReadLoad                ;(R4->LR)
 ]
        MOVNE   R7, LR
        BL      ReadLen                 ;(R3,R4->LR)
        MOV     R4, LR
        MOV     R3, R7
        BL      IndDiscOp               ;(R1-R4->R0,R3-R5,V)
        Pull    "R3,R4"
        B       %FT99
95
        BL      SetVOnR0
99
        B       OsFileBack



; ==========
; SaveCreate
; ==========

; Common object creation code for save, create, openout, copy

; entry
;  Z=0 <=> obj of this name already exists, if so C=1 <=> Dir
;  R1  -> last term of name
;  R3  =  dir ind disc address, IN DIR BUFFER ON ENTRY
;  R4  -> entry in dir
;  R5  -> dir start
;  R6  -> dir end
;  R7  =  load address
;  R8  =  exec address
;  R9  =  RAM start
;  R10 =  RAM end
;  R11 =  reason code, real or dummy OsFile

; exit
;  R0  =  result code

SaveCreate ROUT
        Push    "R1-R11,LR"
 [ Debug7
 DREG   r11, "SaveCreate(",cc
 DLINE  ")"
 ]
        SUB     R10,R10,R9      ;convert end to length
        BNE     %FT10           ;object of this name already exists
        BL      IsDirFull       ;(R3,R5,R6->R0,V)
        BLVC    BeforeAlterFsMap      ;(R3)
        BVS     %FT99
        B       %FT20
10
;file already exists check various restrictions
        MOVCS   R0, #TypesErr    ;cant create file if dir of same name exists
        BLCS    SetV
        BCS     %FT99
        BL      LockedOrOpen    ;refuse to overwrite open files
        BLVC    BeforeAlterFsMap;(R3->R0,V)
        BVS     %FT99

 [ Debug7
 DLINE  "Object already exists"
 ]
;To avoid reloading of FS map and dir after failure of space claim, check if
;enough space will be available after deleting old object

        BL      PrelimFreeCheck ;(R3-R6,R10->R0,R2,C,V)
        BVS     %FT95
        BCS     %FT30           ;no change in disc space allocated needed
15
;        BL      EnsureNewFloppyId       ;(R3->R0,V)
        BLVC    RemoveDirEntry  ;(R3,R4,R5->R0,V)
;        BLVC    WriteDirThenFsMap ;(->R0,V)
        BVS     %FT95
20
        BL      ClaimFreeSpace  ;(R3,R10,R11->R0,R2,R10,V)
        BVS     %FT95
        BL      MakeDirSpace    ;(R3-R6)
;        MOVS    R0,#0,2         ;C=0
30
 [ Debug7
 DLINE  "Making a new DirEntry"
 ]
;C=1 if reusing same space
        MOV     R0,#WriteBit :OR: ReadBit
        BL      MakeDirEntry    ;(R0-R8,R10)
        ASSERT  fsfile_Save<&100 ;to preserve C
        TEQS    R11,#fsfile_Save ;only write file if save
        BNE     %FT40
        Push    "R1-R5"
        MOV     R1,#WriteSecsOp
;        ORRCS   R1,R1,#NoEscape ;if reusing same space
        MOV     R3, R9
        MOVS    R4, R10
        BLNE    IndDiscOp       ;(R1-R4->R0-R5) only if non zero length
        Pull    "R1-R5"
        BVS     %FT95
40
        BL      WriteFsMapThenDir ;(->R0,V)
95
        BL      UnlockMap
99
 [ Debug7
 DLINE  "SaveCreate done"
 ]
        Pull    "R1-R11,PC"


; <<< CRITICAL CODE MAINTAINANCE ROUTINES >>>

; -----------------
; StartCriticalCode
; -----------------

;starts a section of critical code, by setting up a new level on the two copy
;resume address stacks

; corrupts R7-R10

;critical code should start
;       MOV     R11,LR                  ;note parents return & resume address
;       BL      StartCritical

        MACRO
        StartCritical
        MOV     R11,LR
        BL      StartCritical
        MEND


;ONLY FOR USE WITHIN SUBROUTINES CONFORMING TO CRITICAL CODE STRUCTURE

StartCritical
        MOV     R7, #&FF                ;invalid code
        MOV     R8, #0                  ;valid code
        LDR     R9, CriticalSP1
        LDR     R10,CriticalSP2

        STRB    R7, CriticalGood1       ;invalidate 1st copy
        STR     LR, [R9,#4] !           ;stack callers resume address
        STR     R9, CriticalSP1         ;save new stack pointer
        STR     R11,[R9,#-4]            ;update callers parent resume address
        STRB    R8, CriticalGood1       ;validate 1st copy

        STRB    R7, CriticalGood2       ;invalidate 2nd copy
        STR     LR, [R10,#4] !          ;stack callers resume address
        STR     R10,CriticalSP2         ;save new stack pointer
        STR     R11,[R10,#-4]           ;update callers parent resume address
        STRB    R8, CriticalGood2       ;validate 2nd copy

        MOVS    PC,LR


; -----------------
; SetResumeCritical
; -----------------

;set critical code resume at this subroutine level to be callers return address

;ONLY FOR USE WITHIN SUBROUTINES CONFORMING TO CRITICAL CODE STRUCTURE

;corrupts R7-R10

SetResumeCritical
        MOV     R7, #&FF                ;invalid code
        MOV     R8, #0                  ;valid code
        LDR     R9, CriticalSP1
        LDR     R10,CriticalSP2

        STRB    R7, CriticalGood1       ;invalidate 1st copy
        STR     LR, [R9]                ;update resume at this level
        STRB    R8, CriticalGood1       ;validate 1st copy

        STRB    R7, CriticalGood2       ;invalidate 2nd copy
        STR     LR, [R10]               ;update resume at this level
        STRB    R8, CriticalGood2       ;validate 2nd copy

        MOVS    PC,LR


; ============
; ExitCritical
; ============

;leave a piece of critical code

;ONLY FOR USE WITHIN SUBROUTINES CONFORMING TO CRITICAL CODE STRUCTURE

;corrupts R7-R10

ExitCritical
        MOV     R7, #&FF                ;invalid code
        MOV     R8, #0                  ;valid code
        LDR     R9, CriticalSP1
        LDR     R10,CriticalSP2

        STRB    R7, CriticalGood1       ;invalidate 1st copy
        SUB     R9, R9, #4              ;retract resume stack
        STR     R9, CriticalSP1
        STRB    R8, CriticalGood1       ;validate 1st copy

        STRB    R7, CriticalGood2       ;invalidate 2nd copy
        SUB     R10,R10,#4              ;retract resume stack
        STR     R10,CriticalSP2
        STRB    R8, CriticalGood2       ;validate 2nd copy

        LDMDA   R9,{PC}


; ============
; DisableBreak
; ============

DisableBreak
        Push    "R0-R2,LR"
 [ Debugb
        DREG    lr, "Break off from ",cc
 ]
        MOV     R0, #OsbyteBreakAction
        MOV     R1, #4_2222
        MOV     R2, #0
        BL      OnlyXOS_Byte
 [ Debugb
        DREG    r1, " - old action "
 ]
        STRB    R1, BreakAction
        Pull    "R0-R2,PC",,^


; ============
; RestoreBreak
; ============

RestoreBreak
        Push    "R0-R2,LR"
        MOV     R0, #OsbyteBreakAction
        LDRB    R1, BreakAction
 [ Debugb
        DREG    lr, "Break on from ",cc
        DREG    r1, " - restoring action "
 ]
        MOV     R2, #0
        BL      OnlyXOS_Byte
        Pull    "R0-R2,PC",,^


; <<< THE CRITICAL ROUTINES >>>

; Unlike other routines in this module only the contents of registers which
; return results are defined after calls to these routines

; ------------------
; CriticalWriteFsMap
; ------------------

; entry: disc rec in CritDiscRec
;        drv  rec in CritDrvRec

; exit: result code in CritResult (and R0)

CriticalWriteFsMap ROUT
        StartCritical
        LDR     R5, CritDiscRec
        LDR     R6, CritDrvRec
        LDR     R3, [R5,#RootDir]
        LDRB    R0, [R5,#DiscFlags]
        ASSERT  NeedNewIdFlag = 1 :SHL: 1
        MOVS    R0, R0, LSR #1+1        ;C set <=> need new id
 [ NewFs
        BL      TestMap                 ;(R3->Z) preserves Z
        BNE     %FT70

;HERE IF WRITING NEW MAP
        LDR     R10,[R6,#DrvsFsMap]
        MOVCS   R1, #(ZoneHead*8)+Zone0Bits
        BLCS    MarkZone
        LDRCSB  R0, [R10,#ZoneHead+DiscId]      ;change id if needed
        ADDCS   R0, R0, #1
        STRCSB  R0, [R10,#ZoneHead+DiscId]
        CMPCSS  R0, #&100
        LDRCSB  R0, [R10,#ZoneHead+DiscId+1]
        ADDCS   R0, R0, #1
        STRCSB  R0, [R10,#ZoneHead+DiscId+1]

        LDRB    R9, [R5,#Zones]
        LDRB    R7, [R5,#SectorSize]
        MOV     R1, #1
        MOV     R1, R1, LSL R7          ;sector size
        ADD     R8, R10,R9, LSL R7      ;-> zone flags
        MOV     R3, #-1                 ;init first modified zone
        MOV     R4, #-2                 ;init last modified zone

        MOV     R0, #0
05
        LDRB    LR, [R8,R0]
        TSTS    LR, #ZoneValid
        BNE     %FT10
        CMPS    R3, #-1
        MOVEQ   R3, R0
        MOV     R4, R0
10
        ADD     R0, R0, #1
        CMPS    R0, R9
        BLO     %BT05
        CMPS    R3, R4
        BGT     %FT93           ;if no zones modified

        ADDLT   R3, R10,R3, LSL R7      ;if more than one zone modified
        ADDLT   R4, R10,R4, LSL R7      ;then alter cross check bytes
        LDRLTB  R0, [R4,#CrossCheck]
        LDRLTB  LR, [R3,#CrossCheck]
        EORLT   R0, R0, LR
        ADDLT   LR, LR, #1
        EORLT   R0, R0, LR
        STRLTB  LR, [R3,#CrossCheck]
        STRLTB  R0, [R4,#CrossCheck]

        MOV     R2, #0
15
        MOV     R3, #bit31      ;init offset to start writing to
        MOV     R4, #bit30
20
        SUBS    R11,R2, R9      ;C=0 <=> first copy
        MOVMI   R11,R2

        LDRB    LR, [R8,R11]
        TSTS    LR, #ZoneValid   ;preserves C
        BNE     %FT30
        ADDCC   R0, R10,R11,LSL R7
        BLCC    SetNewCheck     ;(R0,R1) need to write zone if modified

        ADD     R4, R1, R2, LSL R7      ;  note end
        TEQS    R3, #bit31
        MOVEQ   R3, R2, LSL R7          ;  and start if none already
30
        SUB     LR, R2, R4, LSR R7
        ADD     R2, R2, #1
        CMPS    R3, #0
        LDRGEB  R0, [R5,#SecsPerTrk]
        CMPGES  LR, R0
        CMPLTS  R2, R9, LSL #1
        BLT     %BT20

        TEQS    R3, #bit31
        BEQ     %FT35
        Push    "R1,R2,R8"
        BL      MapDiscAdd              ;(R5,R7,R9->R2)
 [ BigDisc
        ADD     R2, R2, R3, LSR R7      ;disc add to start writing at
 |
        ADD     R2, R2, R3              ;disc add to start writing at
 ]

        SUBS    LR, R4, R9, LSL R7      ;length(s) to write
        RSBHIS  R8, R3, R9, LSL R7
        SUBLS   R8, R4, R3

        SUB     R4, R4, R3              ;total amount to write
        SUBS    R3, R3, R9, LSL R7      ;ensure start offset in first copy
        ADDMI   R3, R3, R9, LSL R7

        ADD     R0, R10,R3              ;where to start in map
 [ DebugE
        Push    "r0"
        DREG    R0, "(add:",cc
        DREG    R8, ", len:",cc
        DREG    R10, ") (add:",cc
        MOV     r0, lr
        DREG    r0, ", len:",cc
        DLINE   ")"
        Pull    "r0"
 ]
        Push    "R0,R8,R10,LR"   ;stack scatter list (add,len)[,(add,len)]
        MOV     R1, #WriteSecsOp :OR: NoEscape :OR: ScatterBit
        MOV     R3, SP
        BL      RetryDiscOp     ;(R1-R4->R0,R2-R4,V)
        ADD     SP, SP, #4*4
        Pull    "R1,R2,R8"
        BVS     %FT95

        CMPS    R2, R9, LSL #1    ;loop until both copies done
        BLT     %BT15

35
        SUBS    R9, R9, #1
        LDRPLB  LR, [R8,R9]
        ORRPL   LR, LR, #ZoneValid
        STRPLB  LR, [R8,R9]
        BPL     %BT35

        LDRB    R1, [R10,#ZoneHead+DiscId]      ;copy new disc id to disc rec
        LDRB    R2, [R10,#ZoneHead+DiscId+1]
        B       %FT90


70      ;HERE IF WRITING OLD MAP
 ]

        BLCS    InvalidateFsMap         ;(R3)

; C=1 <=> need to update disc id

        MOV     R1, #WriteSecsOp :OR: Atomic
        LDR     R2, [R5,#RootDir]       ;old map at 0
        AND     R2, R2, #DiscBits
        LDR     R7, [R6,#DrvsFsMap]
        BIC     R3, R7, #HiFsBits       ;RAM add
        MOV     R8, R3
        LDRCSB  R0, [R8,#OldId]         ;change id if needed
        ADDCS   R0, R0, #1
        STRCSB  R0, [R8,#OldId]
        CMPCSS  R0, #&100
        LDRCSB  R0, [R8,#OldId+1]
        ADDCS   R0, R0, #1
        STRCSB  R0, [R8,#OldId+1]
        ANDS    R0, R7, #EmptyFs        ;already ensured
        BLEQ    ClearV
        BEQ     %FT95

        BL      UpdateFsMapCheckSum

        MOV     R4, #SzOldFs
        BL      RetryDiscOp             ;(R1-R4->R0-R4,V)
        BVS     %FT95

        STR     R8, [R6,#DrvsFsMap]
        LDRB    R1, [R8,#OldId]          ;copy new disc id to disc rec
        LDRB    R2, [R8,#OldId+1]
90
        STRB    R1, [R5,#DiscId]
        STRB    R2, [R5,#DiscId+1]
        LDRB    LR, [R5,#DiscFlags]
 [ NewFs
        BIC     LR, LR, #NeedNewIdFlag :OR: AltMapFlag
 |
        BIC     LR, LR, #NeedNewIdFlag
 ]
        STRB    LR, [R5,#DiscFlags]
93
        MOV     R0, #0
95
        STR     R0, CritResult
        BL      ExitCritical

; ----------------
; CriticalWriteDir
; ----------------

; entry: ind disc address to write dir buffer to in CritBufDir

; exit: result code in CritResult (and R0)

CriticalWriteDir ROUT
        StartCritical
        LDR     R0, BufDir
        CMPS    R0, #-1
        MOVNE   R0, #0
        BLNE    ClearV
        BNE     %FT95           ;already ensured
        LDR     R3, CritBufDir
        sbaddr  R5, DirBuffer
        BL      TestDir      ;(R3->LR,Z)
        MOVEQ   R9, #NewDirSize
        MOVNE   R9, #OldDirSize
 [ NewDirEntrySz=OldDirEntrySz
 |
        MOVEQ   R8, #NewDirEntrySz
        MOVNE   R8, #OldDirEntrySz
 ]
        ADD     R6, R5, R9
        ADDEQ   R7 ,R6, #NewDirLastMark
        ADDNE   R1, R6, #OldDirLastMark
        BL      IncDirSeqNum    ;increment master sequence number (R5,R6)
        BL      QuickCheckDir   ;(R5,R6->R0,V) all except check byte
        BVS     %FT95           ;Dont write out corrupt dirs

        BL      EndDirEntries   ;(R3,R5,R6->R0)
        MOV     R4,R0

        BL      TestDir         ;(R3->LR,Z)
        BNE     %FT10

;to make disc scavenging easier repeat as many entries as possible at dir end
;for new format

        SUB     LR, R7, #NewDirEntrySz
        SUBS    LR, LR, R4      ;LR=space for repeating dir entries
        ADD     R0, R5, #DirFirstEntry
        SUBHIS  R2, R4, R0      ;R2=length of file list
        MOVLS   R2, #0          ;no space or no files

        CMPS    LR, R2
        MOVLO   R2, LR          ;amount to move = min (space,file list length)
        SUBS    R1, R7, R2      ;dest start
        BL      BlockMove       ;(R0-R2)
10
        MOV     R0, R4           ;clear out any rubbish in dir
        SUBS    R1, R1, R4
        BLHI    ZeroRam         ;(R0,R1)
        BL      TestDirCheckByte;(R3,R5,R6->LR)
        STRB    LR, [R6,#DirCheckByte]

        MOV     R1, #WriteSecsOp:OR: Atomic
        MOV     R2, R3          ;disc add
        MOV     R3, R5          ;RAM add
        MOV     R4, R9          ;length
        BL      IndDiscOp       ;(R1-R4->R0,R3-R5)
        BLVC    ValidateBufDir
        MOVVC   R0,#0
95
        STR     R0, CritResult
        BL      ExitCritical


; -------------------------
; CriticalWriteDirThenFsMap
; -------------------------

CriticalWriteDirThenFsMap ROUT
        StartCritical
        BL      CriticalWriteDir
        LDR     R0, CritResult
        BL      SetVOnR0
        BLVC    CriticalWriteFsMap
        LDRVC   R0, CritResult
        BLVC    SetVOnR0
95
        BL      ExitCritical


; -------------------------
; CriticalWriteFsMapThenDir
; -------------------------

CriticalWriteFsMapThenDir ROUT
        StartCritical
        BL      CriticalWriteFsMap
        LDR     R0, CritResult
        BL      SetVOnR0
        BLVC    CriticalWriteDir
        LDRVC   R0, CritResult
        BLVC    SetVOnR0
95
        BL      ExitCritical

        LTORG
        END
